﻿using System;
using System.Linq;
using App.Account.DataAccess.Interfaces;
using App.Utility.Paging;
using StructureMap;
using App.Utility.Services;
using App.Account.Models;
using App.Utility;
using System.Collections.Generic;

namespace App.Account.Services
{
    public class AccountService : IAccountService
    {
        IAccountRepository accountRepository;

        public AccountService()
        {
            accountRepository = ObjectFactory.GetInstance<IAccountRepository>();
        }

        public App.Account.Models.User GetUser(Guid ID)
        {
            return accountRepository.GetUsers()
                .Where(u => u.ID == ID)
                .SingleOrDefault();
        }


        public App.Account.Models.Role GetRole(Guid ID)
        {
            return accountRepository.GetRoles()
                .Where(r => r.ID == ID)
                .SingleOrDefault();
        }

        public ServiceResponse<User> GetUserByName(string UserName)
        {
            return new ServiceResponse<User>
            {
                Response = accountRepository.GetUsers()
                .Where(u => u.Name == UserName)
                .SingleOrDefault()
            };
        }


        public ServiceResponse<Role> GetRoleByName(string RoleName)
        {
            return new ServiceResponse<Role>
            {
                Response = accountRepository.GetRoles()
                .Where(u => u.Name == RoleName)
                .SingleOrDefault()
            };
        }

        public App.Utility.Paging.PageResponse<App.Account.Models.User> GetUserPage(App.Utility.Paging.PageRequest PageRequest)
        {
            return accountRepository.GetUsers().ToPageResponse(PageRequest);
        }

        public PageResponse<App.Account.Models.Role> GetRolePage(PageRequest PageRequest)
        {
            return accountRepository.GetRoles().ToPageResponse(PageRequest);
        }


        public void SaveUser(App.Account.Models.User user)
        {
            accountRepository.SaveUser(user);
        }


        public void SaveRole(App.Account.Models.Role role)
        {
            accountRepository.SaveRole(role);
        }

        public void DeleteUser(App.Account.Models.User user)
        {
            accountRepository.DeleteUser(user);
        }

        public void DeleteRole(App.Account.Models.Role role)
        {
            accountRepository.DeleteRole(role);
        }

        public ServiceResponse AttemptChangePassword(App.Account.Models.Forms.ChangePassword Form)
        {
            ServiceResponse response = new ServiceResponse() { RuleViolations = new System.Collections.Generic.List<App.Utility.RuleViolation>()};

            response.RuleViolations.AddRange(Form.GetRuleViolations());

            if(!response.Successful)
                return response;

            ServiceResponse<User> loginResponse = AttemptLogin(
                new App.Account.Models.Forms.Login()
                        {
                            UserName = Form.UserName,
                            Password = Form.CurrentPassword
                        });
            response.AppendResponse(loginResponse);

            if (!response.Successful)
                return response;

            User user = loginResponse.Response;
            user.Password = Form.NewPassword;
            accountRepository.SaveUser(user);

            return response;
        }

        public ServiceResponse<User> AttemptLogin(App.Account.Models.Forms.Login Form)
        {
            ServiceResponse<User> response = new ServiceResponse<User>() { Response = null, RuleViolations = new System.Collections.Generic.List<App.Utility.RuleViolation>() };

            response.RuleViolations.AddRange(Form.GetRuleViolations());

            if (!response.Successful)
                return response;

            response.Response = accountRepository.GetUsers()
                .Where(u => u.Password == Form.Password && u.Name == Form.UserName)
                .SingleOrDefault();

            if (response.Response == null)
                response.RuleViolations.Add(new RuleViolation() { PropertyName = "UserName", ErrorMessage = "Invalid login" });

            return response;
        }

        public ServiceResponse<User> AttemptRegister(App.Account.Models.Forms.Register Form)
        {
            ServiceResponse<User> response = new ServiceResponse<User>() { Response = null, RuleViolations = new System.Collections.Generic.List<App.Utility.RuleViolation>() };

            response.RuleViolations.AddRange(Form.GetRuleViolations());

            if (!response.Successful)
                return response;

            if (accountRepository.GetUsers()
                .Where(u => u.Name == Form.UserName.Trim()
                    || u.Email == Form.Email.Trim())
                .Count() > 0)
                response.RuleViolations.Add(new RuleViolation()
                {
                    PropertyName = "UserName",
                    ErrorMessage = "An account already exists for the given credentials"
                });

            if (!response.Successful)
                return response;

            User user = new User()
            {
                Email = Form.Email.Trim(),
                ID = Guid.NewGuid(),
                Name = Form.UserName.Trim(),
                Password = Form.Password
            };

            accountRepository.SaveUser(user);

            response.Response = user;

            return response;                   
        }


        public ServiceResponse<User> AttemptUserEdit(App.Account.Models.Forms.UserForm Form)
        {
            ServiceResponse<User> response = new ServiceResponse<User>();

            response.RuleViolations.AddRange(Form.GetRuleViolations());

            List<Role> roles = new List<Role>();
            if (Form.Roles != null)
            {
                roles = (from r in accountRepository.GetRoles()
                                    where Form.Roles.Contains(r.ID)
                                    select r).ToList();

                if (roles.Count != Form.Roles.Count)
                    response.RuleViolations.Add(new RuleViolation
                    {
                        ErrorMessage = "Invalid RoleID provided",
                        PropertyName = "Roles"
                    });
            }


            if (!response.Successful)
                return response;


            if (Form.UserID.HasValue)
            {
                User user = GetUser(Form.UserID.Value);

                if (user == null)
                    response.RuleViolations.Add(new RuleViolation
                    {
                        PropertyName = "UserID",
                        ErrorMessage = "User not found by UserID"
                    });

                User userByName = GetUserByName(Form.UserName).Response;
                if (userByName != null)
                    if (userByName.ID != user.ID)
                        response.RuleViolations.Add(new RuleViolation
                        {
                            PropertyName = "UserName",
                            ErrorMessage = "Desired UserName is not available"
                        });

                User userByEmail = accountRepository.GetUsers().Where(u => u.Email == Form.Email).SingleOrDefault();
                if (userByName != null)
                    if (userByEmail.ID != user.ID)
                        response.RuleViolations.Add(new RuleViolation
                        {
                            PropertyName = "Email",
                            ErrorMessage = "Desired Email is not available"
                        });

                if (!response.Successful)
                    return response;

                user.Email = Form.Email;
                user.Name = Form.UserName;
                user.Password = Form.Password;
                user.Roles = roles;

                SaveUser(user);

                response.Response = user;

            }
            else
            {

                User userByName = GetUserByName(Form.UserName).Response;
                if (userByName != null)
                    response.RuleViolations.Add(new RuleViolation
                    {
                        PropertyName = "UserName",
                        ErrorMessage = "Desired UserName is not available"
                    });

                User userByEmail = accountRepository.GetUsers().Where(u => u.Email == Form.Email).SingleOrDefault();
                if (userByName != null)
                    response.RuleViolations.Add(new RuleViolation
                    {
                        PropertyName = "Email",
                        ErrorMessage = "Desired Email is not available"
                    });

                if (!response.Successful)
                    return response;

                User user = new User
                {
                    Email = Form.Email,
                    ID = Guid.NewGuid(),
                    Name = Form.UserName,
                    Password = Form.Password,
                    Roles = roles
                };

                SaveUser(user);

                response.Response = user;
            }

            return response;
        }



        public ServiceResponse<Role> AttemptRoleEdit(App.Account.Models.Forms.RoleForm Form)
        {
            ServiceResponse<Role> response = new ServiceResponse<Role>();

            response.RuleViolations.AddRange(Form.GetRuleViolations());

            if (!response.Successful)
                return response;


            if (Form.RoleID.HasValue)
            {
                Role role = GetRole(Form.RoleID.Value);

                if (role == null)
                    response.RuleViolations.Add(new RuleViolation
                    {
                        PropertyName = "RoleID",
                        ErrorMessage = "User not found by RoleID"
                    });

                Role roleByName = GetRoleByName(Form.RoleName).Response;
                if (roleByName != null)
                    if (roleByName.ID != role.ID)
                        response.RuleViolations.Add(new RuleViolation
                        {
                            PropertyName = "RoleName",
                            ErrorMessage = "Desired RoleName is not available"
                        });

                if (!response.Successful)
                    return response;

                role.Level = Form.Level;
                role.Name = Form.RoleName;

                SaveRole(role);

                response.Response = role;

            }
            else
            {

                Role roleByName = GetRoleByName(Form.RoleName).Response;
                if (roleByName != null)
                        response.RuleViolations.Add(new RuleViolation
                        {
                            PropertyName = "RoleName",
                            ErrorMessage = "Desired RoleName is not available"
                        });


                if (!response.Successful)
                    return response;

                Role role = new Role
                {
                    ID = Guid.NewGuid(),
                    Level = Form.Level,
                    Name = Form.RoleName
                };

                SaveRole(role);

                response.Response = role;
            }

            return response;
        }


        public void Dispose()
        {
            // nothing to do
        }
    }
}
